﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using GrpcCommon.Helpers;
using GrpcCommon.Models;
using GrpcService;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace GrpcClient.Test
{
    internal class LongLivedUnit
    {
        private static ServerInfo _serverInfo1 = new ServerInfo();

        /// <summary>
        /// 程序入口
        /// </summary>
        public static void Run()
        {
            try
            {
                // 获取可用服务器并随机链接
                var serverList = GetServerList().Result;
                if (serverList.Any())
                {
                    Console.WriteLine("获取服务器列表成功");
                    Random random = new Random();
                    var index = random.Next(serverList.Count);
                    _serverInfo1 = serverList[index];

                    // 注册设备
                    var registerRes = RegisterDevice().Result;
                    // 订阅设备
                    Subscribe(registerRes[0], registerRes[1], registerRes[2]);
                }
                else
                {
                    Console.WriteLine("没有可用服务器");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"主程序异常,{e.StackTrace}");
                throw;
            }
        }

        /// <summary>
        /// 查询所有可用服务器
        /// </summary>
        /// <returns></returns>
        private static async Task<List<ServerInfo>> GetServerList()
        {
            var serverList = new List<ServerInfo>();
            var httpClient = new HttpClient();
            var deviceListUrl = "http://192.168.10.215:8500/v1/agent/services";
            httpClient.DefaultRequestHeaders.Add("X-Consul-Token", "c10dbaf5-7da5-eb1a-61bc-d2e2c7b3e05c");
            var response = await httpClient.GetAsync(deviceListUrl);
            var resp = await response.Content.ReadAsStringAsync();
            var jObj = JsonConvert.DeserializeObject<JObject>(resp);
            if (jObj == null) return serverList;

            foreach (var obj in jObj)
            {
                var deviceJson = obj.Value?.ToString();
                if (string.IsNullOrEmpty(deviceJson)) continue;

                var serverInfo = JsonConvert.DeserializeObject<ServerInfo>(deviceJson);
                if (serverInfo != null)
                {
                    serverList.Add(serverInfo);
                }
            }
            return serverList;
        }

        /// <summary>
        /// 创建客户端链接
        /// </summary>
        /// <returns></returns>
        private static LonglivedServer.LonglivedServerClient CreateClient(bool enableSsl = true)
        {
            var serverUrl = $"{_serverInfo1.Address}:{_serverInfo1.Port}";
            Channel channel;

            if (enableSsl)
            {
                // tls加密连接
                Console.WriteLine($"尝试链接服务器,https://{serverUrl}");
                var credentials = new SslCredentials(File.ReadAllText("cert/cert.pem"));
                channel = new Channel(_serverInfo1.Address, _serverInfo1.Port, credentials, new List<ChannelOption>()
                {
                    new ChannelOption(ChannelOptions.SslTargetNameOverride,"outlook.office.com")
                });
            }
            else
            {
                // 不安全连接
                Console.WriteLine($"尝试链接服务器,http://{serverUrl}");
                channel = new Channel(serverUrl, ChannelCredentials.Insecure);
            }

            Console.WriteLine("服务器链接成功");
            return new LonglivedServer.LonglivedServerClient(channel);
        }

        /// <summary>
        /// 注册设备
        /// </summary>
        private static async Task<string[]> RegisterDevice()
        {
            try
            {
                Console.WriteLine("开始注册设备");
                var client = CreateClient();

                // 设备详细
                var deviceId = Guid.NewGuid().ToString();
                var randomName = DataHelper.RandomString();
                var accountName = $"{randomName}@wlync.com";
                var token = JwtHelper.GenerateJwt(deviceId, accountName);
                Console.WriteLine($"Token: {token}");
                var deviceDetail = new Struct
                {
                    Fields =
                    {
                        ["name"] = Value.ForString(randomName),
                        ["deviceId"] = Value.ForString(deviceId),
                        ["os"] = Value.ForString("windows 11"),
                        ["manufactory"] = Value.ForString(randomName),
                        ["model"] = Value.ForString(randomName),
                        ["systemSKU"] = Value.ForString(randomName),
                        ["majorVersion"] = Value.ForString("22621.1105"),
                        ["buildLab"] = Value.ForString(randomName),
                    }
                };
                var register = await client.RegisterDeviceAsync(
                    new DeviceRequest
                    {
                        DeviceId = deviceId,
                        AccountName = accountName,
                        DeviceDetail = deviceDetail,
                        Authtoken = token
                    });
                Console.WriteLine($"设备{randomName}({deviceId})注册完成: Code={register.Code},Registration={register.Registration},Correlationid={register.Correlationid}");

                return new[] { deviceId, accountName, register.Correlationid };
            }
            catch (Exception e)
            {
                Console.WriteLine($"注册设备异常,{e.StackTrace}");
                throw;
            }
        }

        /// <summary>
        /// 订阅设备
        /// </summary>
        private static async void Subscribe(string deviceId, string accountName, string correlationId)
        {
            Console.WriteLine("开始订阅设备");
            var client = CreateClient();
            var token = JwtHelper.GenerateJwt(deviceId, accountName);
            Console.WriteLine($"Token: {token}");
            var subscribe = client.Subscribe(new Request
            {
                DeviceId = deviceId,
                AccountName = accountName,
                Correlationid = correlationId,
                Authtoken = token
            });

            try
            {
                var lastTime = DateTime.Now;
                while (await subscribe.ResponseStream.MoveNext())
                {
                    var nowTime = DateTime.Now;
                    var seconds = (nowTime - lastTime).TotalSeconds.ToString("F2");
                    // 重置上次响应时间
                    lastTime = nowTime;
                    var resp = subscribe.ResponseStream.Current;
                    Console.WriteLine(resp != null
                        ? $"响应时间:{seconds}秒, 订阅成功, Code={resp.Code},Action={resp.Action},Correlationid={resp.Correlationid},Message={resp.Message}"
                        : $"响应时间:{seconds}秒, 订阅失败");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"订阅异常,{e.StackTrace}");
                throw;
            }
        }
    }
}
